#include "sniffing.h"
#include "stats.h"

void
got_packet(u_char *args, const struct pcap_pkthdr* header, const u_char* packet, struct timeval* time_begin_capture)
{	
	// Compteur du nombre de paquets
	static int counter = 1;
	// Temps actuel
	struct timeval now;
	gettimeofday(&now, NULL);
	int duration;

	// On remplit la structure de stats
	sniff_stat* stats = (sniff_stat*) args;
	stats->packet_id = counter;
	
	// Pointeurs sur les headers du paquet
	const struct sniff_ethernet* ethernet;
	const struct sniff_ip*       ip;
	const struct sniff_ipv6*     ipv6;
	const struct sniff_tcp*      tcp;
	const struct sniff_udp*      udp;
	
	// Taille des parties du paquet
	int size_ip;
	
	// Flag pour savoir si le paquet est IPv4 ou IPv6
	int is_ipv4 = 0;
	int is_ipv6 = 0;
	// Flag pour savoir si le paquet est TCP ou UDP
	int is_tcp = 0;
	int is_udp = 0;
	
	printf("\n");
	
	printf("[FRAME]\n");
	printf("\tFrame N°  : %d\n", counter++);
	printf("\tTimestamp : ");
	print_time(header->ts.tv_sec, header->ts.tv_usec);
	printf("\n\tTimer     : ");
	duration = difftime_ms(&now, time_begin_capture)/1000.;
	print_duration(duration);
	printf("\n\tRate      : %.1f packets/s", counter/((duration+1)*1.));
	printf("\n");
	printf("\tSize      : %d bytes\n", header->len);
	
	stats->timestamp = header->ts;
	
	// Partie Ethernet
	ethernet = (struct sniff_ethernet*)(packet);
	
	printf("\n[ETHERNET]\n");
	
	strcpy(stats->eth_src, ether_ntoa((const struct ether_addr *) ethernet->ether_shost));
	strcpy(stats->eth_dst, ether_ntoa((const struct ether_addr *) ethernet->ether_dhost));
	
	printf("\t     Source : %s\n", stats->eth_src);
	printf("\tDestination : %s\n", stats->eth_dst);
	
	printf("\t       Type : ");
	
	// On affiche le type
	stats->eth_type = ntohs(ethernet->ether_type);
	print_eth_type(stats->eth_type);
	printf("\n");
	
	// On regarde si le paquet n'est pas IPv4 ou IPv6
	switch (stats->eth_type)
	{
		case ETHERTYPE_IP :
			is_ipv4 = TRUE;
			break;
			
		case ETHERTYPE_IPV6 :
			is_ipv6 = TRUE;
			break;
	}

	stats->is_ipv4 = is_ipv4;
	stats->is_ipv6 = is_ipv6;
	
	// On s'arrête ici si on a pas un paquet IP ou IPv6
	if (!is_ipv4 && !is_ipv6)
	{
		printf("\n=========================================\n");
		return;
	}
	
	// Si le paquet est de type IPv6
	if (is_ipv6)
	{
		printf("\n[IPv6]\n");
		
		ipv6 = (struct sniff_ipv6*)(packet + SIZE_ETHERNET);
		
		// Affichage IPv6
		inet_ntop(AF_INET6, ipv6->src_addr, stats->ipv6_src, INET6_ADDRSTRLEN);
		printf("\t     From : %s\n", stats->ipv6_src);
		
		inet_ntop(AF_INET6, ipv6->dst_addr, stats->ipv6_dst, INET6_ADDRSTRLEN);
		printf("\t       To : %s\n", stats->ipv6_dst);
		
		printf("\t   Header : ");
		
		stats->ipv6_nh = ipv6->next_header;
		print_ipv6_next_header(stats->ipv6_nh);
		
		// On vérifie qu'on a pas un paquet TCP ou UDP (pour les stats)
		switch (stats->ipv6_nh)
		{
			case IPPROTO_TCP:
				stats->is_tcp = TRUE;
				break;
		
			case IPPROTO_UDP:
				stats->is_udp = TRUE;	
				break;
		}
		
		printf("\n\n=========================================\n");
		return;
	}
	
	printf("\n[IP]\n");
	
	// Partie IP
	ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);	
	size_ip = IP_HL(ip)*4;
	
	// Identifiant du paquet
	stats->ipv4_id = ntohs(ip->ip_id);
	printf("\t       ID : 0x%X\n", stats->ipv4_id);
	
	// Affichage IPv4
	strcpy(stats->ipv4_src, inet_ntoa(ip->ip_src));
	strcpy(stats->ipv4_dst, inet_ntoa(ip->ip_dst));
	printf("\t     From : %s\n", stats->ipv4_src);
	printf("\t       To : %s\n", stats->ipv4_dst);
	
	printf("\t Protocol : ");
	
	stats->ipv4_proto = ip->ip_p;
	print_ipv4_protocol(stats->ipv4_proto);
	printf("\n");
	
	switch (stats->ipv4_proto)
	{
		case IPPROTO_TCP:
			is_tcp = TRUE;
			break;
			
		case IPPROTO_UDP:
			is_udp = TRUE;
			break;
	}
	
	stats->is_tcp = is_tcp;
	stats->is_udp = is_udp;
	
	if (is_tcp)
	{
		printf("\n[TCP]\n");
		
		// On récupère le header TCP
		tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);

		stats->tcp_src = ntohs(tcp->th_sport);
		printf("\tSource port      : %d ", stats->tcp_src);
		print_ipv4_service(stats->tcp_src, "tcp");
		printf("\n");
		
		stats->tcp_dst = ntohs(tcp->th_dport);
		printf("\tDestination port : %d ", stats->tcp_dst);
		print_ipv4_service(stats->tcp_dst, "tcp");
		printf("\n");
		
	}
	else if (is_udp)
	{
		printf("\n[UDP]\n");
		
		// On récupère le header UDP
		udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + size_ip);
		
		stats->udp_src = ntohs(udp->uh_sport);
		printf("\tSource port      : %d ", stats->udp_src);
		print_ipv4_service(stats->udp_src, "udp");
		printf("\n");
		
		stats->udp_dst = ntohs(udp->uh_dport);
		printf("\tDestination port : %d ", stats->udp_dst);
		print_ipv4_service(stats->udp_dst, "udp");
		printf("\n");
		
	}
	
	printf("\n=========================================\n");
}

void
print_time(time_t tv_sec, time_t tv_usec)
{
	struct tm* print_time;
	char str_time[40];
	
	print_time = localtime(&tv_sec);
	//strftime(str_time, sizeof(str_time), "%Y-%m-%d %H:%M:%S", print_time);
	strftime(str_time, sizeof(str_time), "%H:%M:%S", print_time);
	
	printf("%s.%d", str_time, (int)tv_usec);
}

void
print_eth_type(u_short eth_type)
{
	switch (eth_type)
	{
		case ETHERTYPE_LOOPBACK :
			printf("Ethernet LOOPBACK");
			break;
			
		case ETHERTYPE_ARP :
			printf("ARP");
			break;
		
		case ETHERTYPE_REVARP :
			printf("Reverse ARP");
			break;

		case ETHERTYPE_VLAN :
			printf("IEEE 802.1Q VLAN");
			break;
		
		case ETHERTYPE_PUP :
			printf("Xerox PUP");
			break;

		case 0x0004 : //ETHERTYPE_8022
			printf("IEEE 802.2");
			break;
		
		case 0x0001 : //ETHERTYPE_8023
			printf("IEEE 802.3");
			break;
		
		case 0x0005 : //ETHERTYPE_SNAP
			printf("SNAP");
			break;
		
		case ETHERTYPE_IP :
			printf("IP");
			break;
			
		case ETHERTYPE_IPV6 :
			printf("IPv6");
			break;
	
		default :
			printf("Unknown (0x%X - %d)", eth_type, eth_type);
	}
}

void
print_ipv6_next_header(u_int8_t next_header)
{
	switch (next_header)
	{
		case IPPROTO_TCP:
			printf("IPv6 TCP");
			break;
		
		case IPPROTO_UDP:
			printf("IPv6 UDP");
			break;
		
		case IPPROTO_HOPOPTS :
			printf("IPv6 Hop-by-Hop options");
			break;
		
		case IPPROTO_IPV6 :
			printf("IPv6");
			break;
		
		case IPPROTO_ROUTING :
			printf("IPv6 Routing");
			break;
		
		case IPPROTO_FRAGMENT :
			printf("IPv6 Fragmentation");
			break;
		
		case IPPROTO_RSVP :
			printf("IPv6 Resource Reservation Protocol (RSVP)");
		
		case IPPROTO_ESP :
			printf("IPv6 Encapsulating Security Payload (ESP)");
			break;
		
		case IPPROTO_AH :
			printf("IPv6 Authentification");
			break;
		
		case IPPROTO_ICMPV6 :
			printf("ICMPv6");
			break;
		
		case IPPROTO_NONE :
			printf("None");
			break;
		
		case IPPROTO_DSTOPTS :
			printf("IPv6 Destination options");
		
		default :
			printf("Unknown (0x%X)", next_header);
	}
}

void
print_ipv4_protocol(u_char proto)
{
	// Protocole
	struct protoent* protocol;
	
	// Détermination du protocole
	switch (proto)
	{
		case IPPROTO_TCP:
			printf("TCP");
			break;
			
		case IPPROTO_UDP:
			printf("UDP");
			break;
			
		default:
			if ((protocol = getprotobynumber(proto)) != NULL)
			{
				if (protocol->p_aliases[0] != NULL)
				{
					printf("%s", protocol->p_aliases[0]);
				}
				else
				{
					printf("%s", protocol->p_name);
				}
			}
			else
			{
				printf("Unknown (0x%X - %d)", proto, proto);
			}
	}
}

void
print_ipv4_service(u_short port, char* type)
{
	// Service attribué au port
	struct servent* service;
	
	if (strcmp("udp", type) == 0)
	{
		switch (port)
		{
			case 1900 :
				printf("(ssdp) ");
				return;
		}
	}
	
	if ((service = getservbyport(htons(port), type)) != NULL)
	{
		printf("(%s) ", service->s_name);
	}
}


